package com.baseproject.util;


import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import com.baseproject.BaseApplication;
import com.baseproject.helper.IBackgroundThreadTask;
import com.baseproject.helper.ThreadHelper;
import com.tbruyelle.rxpermissions.RxPermissions;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import rx.Observable;

import static com.baseproject.appconfig.AppConfigBaseProject.APPLICATION_NAME;
import static com.baseproject.appconfig.AppConfigBaseProject.FILE_POSTFIX;
import static com.baseproject.helper.ThreadHelper.defaultSubscriber;

/**
 * 管理Memory和File之间的交互
 */
public class FileUtils {

    private static String TAG = "FileUtils";

    public static File writeInputStreamToDisk(InputStream is, String path, String fileName) {
        if (!DeviceUtils.isExternalStorageWritable()) {
            LogUtils.d("external storage is not writable");
            return null;
        }
        try {
            File fileDr = new File(path);
            if (!fileDr.exists()) {
                fileDr.mkdir();
            }
            File file = new File(path, fileName);
            if (file.exists()) {
                file.delete();
                file = new File(path, fileName);
            }
            FileOutputStream fos = new FileOutputStream(path + File.separator + fileName);
            BufferedInputStream bis = new BufferedInputStream(is);
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            fos.flush();
            fos.close();
            bis.close();
            is.close();

            return file;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void Bitmap2File(Bitmap img, String folderPath, String fileName, boolean isCopressed) {
        if (null != img) {
            try {
//              String folderPath = Const.APP_ROOT_DIRECTORY + File.separator + "coverUrl";
                File folder = new File(folderPath);
                if (!folder.exists()) {
                    folder.mkdirs();
                }
                File coverFile = new File(folder, fileName);
                if (coverFile.exists()) {
                    coverFile.delete();
                }
                OutputStream os = new FileOutputStream(folderPath + File.separator + fileName);
                img.compress(Bitmap.CompressFormat.JPEG, isCopressed ? 20 : 100, os);
                os.flush();
                os.close();
            } catch (Exception e) {
                Log.e(TAG, e.getMessage());
                Log.e(TAG, Log.getStackTraceString(new Throwable()));
            }
        }
    }

    public static Bitmap File2Bitmap(String folderPath, String fileName) {
        String coverPath = folderPath;
        if (!isFolderExist(coverPath)) {
            makeDirs(coverPath);
        }
        String allPath = coverPath + File.separator + fileName;
        return new File(allPath).exists() ? BitmapFactory.decodeFile(allPath) : null;
    }

    /**
     * @param path
     * @return 文件path中的文件名前缀
     */
    public static String getFilePerfix(String path) {
        int separatorIndex = path.lastIndexOf("/");
        return path.substring(separatorIndex + 1, path.length());
//        String subString = path.substring(0, separatorIndex - 1);
//        int index = subString.lastIndexOf("/");
//        return (separatorIndex < 0) ? path : path.substring(index + 1, separatorIndex);
    }

    /**
     * 创建文件所在的目录
     *
     * @param filePath 文件目录(文件目录+文件名)
     * @return 是否创建成功
     */
    public static boolean makeDirs(String filePath) {
        String folderName = getFolderName(filePath);
        if (TextUtils.isEmpty(folderName)) {
            return false;
        } else {
            File folder = new File(folderName);
            return (folder.exists() && folder.isDirectory()) ? true : folder.mkdirs();
        }
    }

    /**
     * 创建文件夹
     * @param path 目录(不包含文件名)
     */
    public static void createDirs(String path) {
        if(!isFileExist(path)) {
            File file = new File(path);
            file.mkdirs();
        }
    }

    /**
     * 判断 文件／文件夹 是否存在
     * @param path
     * @return
     */
    public static boolean isFileExist(String path) {
        File file = new File(path);
        return file.exists();
    }

    /**
     * 获取文件目录中的文件夹目录
     *
     * @param filePath
     * @return
     */
    public static String getFolderName(String filePath) {
        if (TextUtils.isEmpty(filePath)) {
            return filePath;
        } else {
            int filePosi = filePath.lastIndexOf(File.separator);
            return (filePosi == -1) ? "" : filePath.substring(0, filePosi);
        }
    }

    /**
     * 判断目录是否存在
     *
     * @param directoryPath 目录的地址
     * @return 是否存在
     */
    public static boolean isFolderExist(String directoryPath) {
        if (TextUtils.isEmpty(directoryPath)) {
            return false;
        } else {
            File dire = new File(directoryPath);
            return (dire.exists() && dire.isDirectory());
        }
    }

    ////////////////////
     //以下取自business层
    /**
     * 获取缓存根目录
     * @param context
     * @return
     */
    public static String getCacheRootPath(Context context) {
        String cacheRootPath = null;
        if(isSdCardAvailable()) {
            cacheRootPath = context.getExternalCacheDir().getPath();
        } else {
            cacheRootPath = context.getCacheDir().getPath();
        }
        return cacheRootPath;
    }

    /**
     * 获取SD Card 根目录
     * @return
     */
    public static String getSdCardRootPath() {
        if(isSdCardAvailable()) {
            return Environment.getExternalStorageDirectory().getPath();
        }
        return null;
    }

    /**
     * 判断SD卡可用
     * @return
     */
    public static boolean isSdCardAvailable() {
        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }

    /**
     * 根据相对文件目录生成对应的完整文件目录
     * @param fileFullPath
     * @return
     */
    public static String getFilePath(String fileFullPath) {
        String[] pathSections = fileFullPath.split("/");
        String cachePath = FileUtils.getCacheRootPath();
        StringBuilder sb = new StringBuilder();
        sb.append(cachePath);
        for (int i = 0; i < pathSections.length - 1; i++) {
            sb.append(File.separator).append(pathSections[i]);
        }
        FileUtils.createDirs(sb.toString());
        String fileName = pathSections[pathSections.length - 1];
        sb.append(File.separator).append(fileName);
        if (!fileName.contains(".")) { //如果文件名尚未制定文件类型
            sb.append(FILE_POSTFIX);
        }
        return sb.toString();
    }

    /**
     * 获取图书缓存根目录
     * @return
     */
    public static String getCacheRootPath() {
        StringBuilder sb = new StringBuilder();
        if(isSdCardAvailable()) {
            String cacheSdRootPath = getSdCardRootPath();
            sb.append(cacheSdRootPath).append(File.separator)
                    .append(APPLICATION_NAME).append(File.separator);
            createDirs(sb.toString());
            return sb.toString();
        } else {
            String cacheRootPath = getCacheRootPath(BaseApplication.getApplication());
            sb.append(cacheRootPath).append(File.separator);
            createDirs(sb.toString());
            return sb.toString();
        }
    }

    /**
     * 创建一个新文件
     * @param file
     */
    public static void createFile(File file) {
        try {
            String filePath = file.getAbsolutePath();
            if(file.exists()) {
                file.delete();
            }
            File fileNewFile = new File(filePath);
            fileNewFile.createNewFile();
        } catch (Exception e) {
            Log.e(TAG, "createFile: ", e);
        }
    }


    private static List<String> filesLock = new ArrayList<>();
    /**
     * 写文件
     * @param file
     * @param fileContent
     */
    public static void writeFile(File file, String fileContent) {
        RxPermissions rxPermissions = new RxPermissions(BaseApplication.getApplication().getCurrentShowingActivity());
        if (!rxPermissions.isGranted("android.permission.WRITE_EXTERNAL_STORAGE")) {
            return;
        }
        ThreadHelper.doInBackground(new IBackgroundThreadTask() {
            @Override
            public Object backgroundCall() {
                String fileLock = getFileLock(file.getPath());
                LogUtils.d("come! fileLock = " + fileLock);
                synchronized (fileLock) {
                    LogUtils.d("enter! fileLock = " + fileLock);
                    createFile(file);
                    try {
                        OutputStream outStream = new FileOutputStream(file);
                        //OutputStreamWriter out = new OutputStreamWriter(outStream, "UTF-8");
                        OutputStreamWriter out = new OutputStreamWriter(outStream);
                        out.write(fileContent);
                        out.close();
                    } catch (Exception e) {
                        Log.e(TAG, "writeFile: file path = " + file.getAbsolutePath(), e);
                    } finally {
                        LogUtils.d("out! fileLock = " + fileLock);
                    }
                }
                return null;
            }
        })
                .subscribe(defaultSubscriber);

    }

    private static String getFileLock(String path) {
        String fileLock;
        if (!filesLock.contains(path)) {
            fileLock = new String(path);
            filesLock.add(fileLock);
        } else {
            fileLock = filesLock.get(filesLock.indexOf(path));
        }
        return fileLock;
    }

    /**
     * 读取文件
     * @param file
     * @return
     */
    public static Observable<String> getStringFromFile(File file) {
        return ThreadHelper.doInBackground(new IBackgroundThreadTask() {
            @Override
            public Object backgroundCall() {
                if (file == null) {
                    return null;
                }
                try {
                    byte[] bytes = getBytesFromFile(file);
                    String charset = getCharset(file.getAbsolutePath());
                    //Log.e(TAG, "getStringFromFile: charset = " + charset + "    " + file.getAbsolutePath());
                    return new String(bytes, charset);
                } catch (Exception e) {
                    Log.e(TAG, "getStringFromFile: ", e);
                }
                return null;
            }
        });
    }

    /**
     * 读取文件
     * @param file
     * @return
     */
    public static byte[] getBytesFromFile(File file) {
        if (file == null) {
            return null;
        }
        try {
            FileInputStream stream = new FileInputStream(file);
            ByteArrayOutputStream out = new ByteArrayOutputStream(1000);
            byte[] b = new byte[1000];
            for (int n; (n = stream.read(b)) != -1; ) {
                out.write(b, 0, n);
            }
            stream.close();
            out.close();
            return out.toByteArray();
        } catch (Exception e) {
            Log.e(TAG, "getBytesFromFile: ", e);
        }
        return null;
    }

    /**
     * 获取文件编码
     * @param fileName
     * @return
     */
    public static String getCharset(String fileName) {
        if(isFileExist(fileName)) {
            BufferedInputStream bis = null;
            String charset = "GBK";
            byte[] first3Bytes = new byte[3];
            try {
                boolean checked = false;
                bis = new BufferedInputStream(new FileInputStream(fileName));

                bis.mark(0);
                int read = bis.read(first3Bytes, 0, 3);
                if (read == -1)
                    return charset;
                if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
                    charset = "UTF-16LE";
                    checked = true;
                } else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
                    charset = "UTF-16BE";
                    checked = true;
                } else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF) {
                    charset = "UTF-8";
                    checked = true;
                }
                bis.mark(0);
                if (!checked) {
                    while ((read = bis.read()) != -1) {
                        if (read >= 0xF0)
                            break;
                        if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的，也算是GBK
                            break;
                        if (0xC0 <= read && read <= 0xDF) {
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
                                // (0x80 - 0xBF),也可能在GB编码内
                                continue;
                            else
                                break;
                        } else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错，但是几率较小
                            read = bis.read();
                            if (0x80 <= read && read <= 0xBF) {
                                read = bis.read();
                                if (0x80 <= read && read <= 0xBF) {
                                    charset = "UTF-8";
                                    break;
                                } else
                                    break;
                            } else
                                break;
                        }
                    }
                }
            } catch (Exception e) {
                Log.e(TAG, "getCharset: ", e);
            } finally {
                if (bis != null) {
                    try {
                        bis.close();
                    } catch (Exception e) {
                        Log.e(TAG, "getCharset: ", e);
                    }
                }
            }
            return charset;
        }
        return "UTF-8";
    }

    public static byte[] getAssertsFile(Context context, String fileName) {
        InputStream inputStream = null;
        AssetManager assetManager = context.getAssets();
        try {
            inputStream = assetManager.open(fileName);
            if (inputStream == null) {
                return null;
            }

            BufferedInputStream bis = null;
            int length;
            try {
                bis = new BufferedInputStream(inputStream);
                length = bis.available();
                byte[] data = new byte[length];
                bis.read(data);

                return data;
            } catch (IOException e) {

            } finally {
                if (bis != null) {
                    try {
                        bis.close();
                    } catch (Exception e) {

                    }
                }
            }

            return null;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static Observable<File> getFile(String destDir, String destFileName) {
        File file = new File(destDir, destFileName);
        if (file.exists()) {
            return Observable.just(file);
        } else {
            return Observable.just(null);
        }
    }
}
